#include <cmd/common.h>
#include "database_td.h"
#include <cassert>
#include <vector>

DB_TdEngine::DB_TdEngine(std::string ip, std::string user, std::string passwd, std::string db){
    std::thread([&]{
        try{
            taos = taos_connect(ip.c_str(), user.c_str(), passwd.c_str(), db.c_str(), 0);
            if (taos == NULL) {
                BOOST_LOG_TRIVIAL(info) << "failed to connect to tdengine, reason:" << taos_errstr(NULL);
            }
        }catch(...){}
    }).detach();
}
std::list<std::any> DB_TdEngine::query(std::string sql){
    TAOS_ROW row;
    std::list<std::any> return_data;
    if (taos == NULL) {
        return return_data;
    }
    TAOS_RES* result = queryDB(taos,sql.c_str());
    int num_fields = taos_field_count(result);
    TAOS_FIELD *fields = taos_fetch_fields(result);
    while ((row = taos_fetch_row(result))) {
        for (int i = 0; i < num_fields; ++i) {
            if (row[i] == NULL) continue;
            switch (fields[i].type) {
                case TSDB_DATA_TYPE_TINYINT:return_data.emplace_back(*((char *)row[i]));break;
                case TSDB_DATA_TYPE_UTINYINT:return_data.emplace_back(*((unsigned char *)row[i]));break;
                case TSDB_DATA_TYPE_SMALLINT:return_data.emplace_back(*((short *)row[i]));break;
                case TSDB_DATA_TYPE_USMALLINT:return_data.emplace_back(*((unsigned short *)row[i]));break;
                case TSDB_DATA_TYPE_INT:return_data.emplace_back(*((int *)row[i]));break;
                case TSDB_DATA_TYPE_UINT:return_data.emplace_back(*((unsigned int *)row[i]));break;
                case TSDB_DATA_TYPE_BIGINT:return_data.emplace_back(*((long long *)row[i]));break;
                case TSDB_DATA_TYPE_UBIGINT:return_data.emplace_back(*((unsigned long long *)row[i]));break;
                case TSDB_DATA_TYPE_FLOAT: return_data.emplace_back(*((float *)row[i]));break;
                case TSDB_DATA_TYPE_DOUBLE: return_data.emplace_back(*((double *)row[i]));break;
                case TSDB_DATA_TYPE_BINARY:
                case TSDB_DATA_TYPE_NCHAR: {
                    int charLen = ((short*)(char*)row[i] - sizeof(short))[0];
                    if (fields[i].type == TSDB_DATA_TYPE_BINARY) {
                        assert(charLen <= fields[i].bytes && charLen >= 0);
                    } else {
                        assert(charLen <= fields[i].bytes * sizeof(int) && charLen >= 0);
                    }
                    return_data.emplace_back(std::string((char*)row[i],charLen));
                } break;
                case TSDB_DATA_TYPE_TIMESTAMP:return_data.emplace_back(*((long long *)row[i]));break;
                case TSDB_DATA_TYPE_BOOL:return_data.emplace_back(*((char *)row[i]));break;
                default:break;
            }
        }
    }
    taos_free_result(result);
    return return_data;
}
void DB_TdEngine::exec(std::string sql){
    if (taos != NULL) {
        TAOS_RES* pSql = queryDB(taos,sql.c_str());
        taos_free_result(pSql);
    }
}
TAOS_RES* DB_TdEngine::queryDB(TAOS *taos, const char *command){
    TAOS_RES *pSql = NULL;
    int32_t   code = -1;

    for (int i = 0; i < 5; i++) {
        if (NULL != pSql) {
            taos_free_result(pSql);
            pSql = NULL;
        }

        pSql = taos_query(taos, command);
        code = taos_errno(pSql);
        if (0 == code) {
            break;
        }
    }
    if (code != 0) {
        BOOST_LOG_TRIVIAL(error) << "Failed to run "<<command<<", reason: "<< taos_errstr(pSql);
        taos_free_result(pSql);
    }
    return pSql;
}